package com.ocm.core.domain;

import java.util.Iterator;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.TableGenerator;

import com.ocm.core.dto.SecurityDto;
import com.ocm.core.exception.ExceptionCode;
import com.ocm.core.exception.OCMRuntimeException;
import com.ocm.core.exception.TypeCodeException;
import com.ocm.core.exception.UnsupportTypeException;

@Entity
@Table(name = "CLASS_SECURITY")
public class Security implements Persistable {

   @Id
   @GeneratedValue(strategy = GenerationType.TABLE, generator = "GEN_SECURITY")
   @TableGenerator(name = "GEN_SECURITY", table = "SEQUENCE_LIST", allocationSize = 1, initialValue = 1, pkColumnName = "NAME", valueColumnName = "NEXT_SEQUENCE", pkColumnValue = "SECURITY")
   @Column(name = "SECURITY_ID")
   private Long id;

   @Column(name = "SECURITYOBJECT")
   @Lob
   private String securityInformation;

   @OneToOne
   private ClassDefinition classDefinition;

   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public ClassDefinition getClassDefinition() {
      return classDefinition;
   }

   public void setClassDefinition(ClassDefinition classDefinition) {
      this.classDefinition = classDefinition;
   }


   public String getSecurityInformation() {
      return securityInformation;
   }

   public void setSecurityInformation(String securityInformation) {
      this.securityInformation = securityInformation;
   }

   public void appendSecurity(Long userId, String newSec){
      if (!hasContainUser(userId)){
         if (this.securityInformation == null || "".equals(this.securityInformation))
            this.securityInformation = "|" + newSec;
         else 
            this.securityInformation = this.securityInformation + "|" + newSec;
      }
   }

   public void updatedPermission(Long userId, int accessMask){
      if (hasContainUser(userId)){
         String[] secProperties = getProperties(userId);
         String newSecObject = "|" + secProperties[0].concat("_");
         newSecObject = newSecObject.concat(secProperties[1]).concat("-");
         newSecObject = newSecObject.concat(""+accessMask);
         this.securityInformation = secProperties[3] + newSecObject + secProperties[4];
      }
   }

   public void removePermission(Long userId){
      if (hasContainUser(userId)){
         String[] secProperties = getProperties(userId);
         this.securityInformation = secProperties[3] + secProperties[4];
      }
   }

   private String[] getProperties(Long userId){
      String pattern = "|" + userId.toString()+"_";
      int indexOf = this.securityInformation.indexOf(pattern);
      String strPart1 = "";
      if (indexOf >= 0)
         strPart1 = this.securityInformation.substring(0, indexOf);
      String strPart2 = this.securityInformation.substring(indexOf+1);
      int nextIndexOf = strPart2.indexOf("|");
      String strSecObject = strPart2;
      String nextStrPart2 = "";
      if (nextIndexOf > 0){
         strSecObject = strPart2.substring(0, nextIndexOf);
         nextStrPart2 = strPart2.substring(nextIndexOf);
      }
      String[] partSec = strSecObject.split("-");
      String[] userSec = partSec[0].split("_");
      String[] secParts = new String[5];
      secParts[0] = userSec[0];
      secParts[1] = userSec[1];
      secParts[2] = partSec[1];
      secParts[3] = strPart1;
      secParts[4] = nextStrPart2;
      return secParts;
   }

   public boolean hasContainUser(Long userId){
      String pattern = "|" + userId.toString() + "_";
      if (this.securityInformation != null && this.securityInformation.contains(pattern)){
         return true;
      }
      return false;
   }

   public static String getSecurity(SecurityDto securityDto) throws UnsupportTypeException{
      int participantType = 0; 
      if (securityDto.getHasAnonymous() == true){
         participantType += ParticipantType.HASANONYMOUS.getParticipantType();
      }
      if (securityDto.getHasCreator() == true){
         participantType += ParticipantType.HASCREATOR.getParticipantType();
      }
      if (securityDto.getHasGroup() == true){
         participantType += ParticipantType.HASGROUP.getParticipantType();
      }
      if (securityDto.getHasUser() == true){
         participantType += ParticipantType.HASUSER.getParticipantType();
      }
      if (participantType == 0){
         throw new UnsupportTypeException(ExceptionCode.instance(TypeCodeException.class,
               TypeCodeException.PARTICIPANTTYPE));
      }
      StringBuffer stringBuffer = new StringBuffer();
      stringBuffer.append(securityDto.getUserId().longValue());
      stringBuffer.append("_" + participantType);
      stringBuffer.append("-" + securityDto.getAccessMask());
      String result = stringBuffer.toString();
      return result;
   }
   
   public static String getDefaultCreatorSecurity(Long creatorId) throws UnsupportTypeException{
      StringBuffer stringBuffer = new StringBuffer();
      stringBuffer.append(creatorId.longValue());
      stringBuffer.append("_" + ParticipantType.HASCREATOR.getParticipantType());
      stringBuffer.append("-" + AccessMask.FULLPERMISSION.getAccessMask());
      String result = stringBuffer.toString();
      return result;
   }

   public SecurityElements getSecurityElements(){
      SecurityElements securityElements = new SecurityElements(this.securityInformation);
      return securityElements;
   }

   public class SecurityElements implements Iterator<SecurityElement>{
      private String element;
      private String restElement;

      private SecurityElements(String strSec) {
         this.element = strSec;
         if (element == null)
            this.restElement = "";
         else
            this.restElement = element;
      }

      public boolean hasNext() {
         if (this.restElement.contains("|"))
            return true;
         return false;
      }

      public SecurityElement next() {
         String strPart = this.restElement.substring(1);
         int indexOf = strPart.indexOf('|');
         this.restElement = "";
         String strSecObject = strPart;
         if (indexOf >= 0){
            this.restElement = strPart.substring(indexOf);
            strSecObject = strPart.substring(0, indexOf);
         }
         String[] partSec = strSecObject.split("-");
         String[] userSec = partSec[0].split("_");
         
         String[] properties = new String[3];
         properties[0]=userSec[0];
         properties[1]=userSec[1];
         properties[2]=partSec[1];
         SecurityElement securityElement =  new SecurityElement(properties);
         return securityElement;
      }

      public void remove() {
         
      }
   }

   public class SecurityElement{

      private Long userId;

      private Boolean hasUser;

      private Boolean hasGroup;

      private Boolean hasCreator;

      private Boolean hasAnonymous;

      private Integer accessMask;

      public SecurityElement(String[] secProperty) {
         this.hasAnonymous = false;
         this.hasCreator = false;
         this.hasGroup = false;
         this.hasUser = false;
         int partType = Integer.parseInt(secProperty[1]);
         try {
            ParticipantType[] participantTypes = ParticipantType.getParticipantTypes(partType);
            for (ParticipantType participantType : participantTypes){
               switch (participantType) {
               case HASANONYMOUS:
                  this.hasAnonymous = true;
                  break;
               case HASCREATOR:
                  this.hasCreator = true;
                  break;
               case HASGROUP:
                  this.hasGroup = true;
                  break;
               case HASUSER:
                  this.hasUser = true;
                  break;
               default:
                  break;
               }
            }
         } catch (UnsupportTypeException e) {
            throw new OCMRuntimeException(e);
         }
         
         this.userId = Long.parseLong(secProperty[0]);
         this.accessMask = Integer.parseInt(secProperty[2]);
      }

      public Long getUserId() {
         return userId;
      }

      public void setUserId(Long userId) {
         this.userId = userId;
      }

      public Boolean getHasUser() {
         return hasUser;
      }

      public void setHasUser(Boolean hasUser) {
         this.hasUser = hasUser;
      }

      public Boolean getHasGroup() {
         return hasGroup;
      }

      public void setHasGroup(Boolean hasGroup) {
         this.hasGroup = hasGroup;
      }

      public Boolean getHasCreator() {
         return hasCreator;
      }

      public void setHasCreator(Boolean hasCreator) {
         this.hasCreator = hasCreator;
      }

      public Boolean getHasAnonymous() {
         return hasAnonymous;
      }

      public void setHasAnonymous(Boolean hasAnonymous) {
         this.hasAnonymous = hasAnonymous;
      }

      public Integer getAccessMask() {
         return accessMask;
      }

      public void setAccessMask(Integer accessMask) {
         this.accessMask = accessMask;
      }
   }
}
